{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Work with Language-Integrated Query (LINQ)\n",
    "\n",
    "This tutorial teaches you features in .NET Core and the C# language. You’ll learn how to:\n",
    "\n",
    "- Generate sequences with LINQ.\n",
    "- Write methods that can be easily used in LINQ queries.\n",
    "- Distinguish between eager and lazy evaluation.\n",
    "\n",
    "You'll learn these techniques by building an application that demonstrates one of the basic skills of any magician: the [faro shuffle](https://en.wikipedia.org/wiki/Faro_shuffle). Briefly, a faro shuffle is a technique where you split a card deck exactly in half, then the shuffle interleaves each one card from each half to rebuild the original deck.\n",
    "\n",
    "Magicians use this technique because every card is in a known location after each shuffle, and the order is a repeating pattern.\n",
    "\n",
    "For your purposes, it is a light hearted look at manipulating sequences of data. The application you'll build constructs a card deck and then performs a sequence of shuffles, writing the sequence out each time. You'll also compare the updated order to the original order.\n",
    "\n",
    "## Create a deck of cards\n",
    "\n",
    "Commonly, a deck of playing cards has four suits, and each suit has thirteen values. Let's create two `enum` types to represent the suits and the ranks, and two extension methods that enumerate each suit and each rank:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "public enum Suit\n",
    "    {\n",
    "        Clubs,\n",
    "        Diamonds,\n",
    "        Hearts,\n",
    "        Spades\n",
    "    }\n",
    "    \n",
    "Console.WriteLine(\".NET Interactive knows about a Suit\");\n",
    "\n",
    " public enum Rank\n",
    "    {\n",
    "        Two,\n",
    "        Three,\n",
    "        Four,\n",
    "        Five,\n",
    "        Six,\n",
    "        Seven,\n",
    "        Eight,\n",
    "        Nine,\n",
    "        Ten,\n",
    "        Jack,\n",
    "        Queen,\n",
    "        King,\n",
    "        Ace\n",
    "    }\n",
    "\n",
    "Console.WriteLine(\".NET Interactive knows about a Face.\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A `Card` is defined by its `Suit` and its `Rank`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "public class Card\n",
    "{\n",
    "    public Suit Suit { get; }\n",
    "    public Rank Rank { get; }\n",
    "\n",
    "    public Card(Suit s, Rank r)\n",
    "    {\n",
    "        Suit = s;\n",
    "        Rank = r;\n",
    "    }\n",
    "\n",
    "    public override string ToString() => $\"{Rank} of {Suit}\";\n",
    "}\n",
    "\n",
    "Console.WriteLine(\".NET Interactive knows about a Card.\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, define a `Deck` class to represent the deck of cards. First, define two  [*iterator methods*](https://docs.microsoft.com/dotnet/csharp/iterators#enumeration-sources-with-iterator-methods) to generate the ranks and suits as sequences of values. These create the sequence of all suits and al ranks. They provide an easy way to enumerate all the possible combinations that make a deck of cards.\n",
    "\n",
    "The `Deck` class has two constructors. The default constructor generate a deck in the sorted order. The second constructor takes a sequence of cards in the specified order. Later, you'll use the second constructor as you shuffle the deck."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "static IEnumerable<Suit> Suits() => Enum.GetValues(typeof(Suit)) as IEnumerable<Suit>;\n",
    "static IEnumerable<Rank> Ranks() => Enum.GetValues(typeof(Rank)) as IEnumerable<Rank>;\n",
    "\n",
    "Console.WriteLine(\".NET Interactive can generate suits and ranks.\");\n",
    "\n",
    "public class Deck\n",
    "{\n",
    "    private List<Card> cards;\n",
    "    \n",
    "    public Deck()\n",
    "    {\n",
    "        var sequence = from s in Suits()\n",
    "                       from r in Ranks()\n",
    "                       select new Card(s, r);\n",
    "        cards = new List<Card>(sequence);\n",
    "    }\n",
    "    public Deck(IEnumerable<Card> cards) => this.cards = new List<Card>(cards);\n",
    "\n",
    "    public IEnumerable<Card> Cards => cards;\n",
    "}\n",
    "\n",
    "Console.WriteLine(\".NET Interactive knows about a Deck of cards\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The multiple `from` clauses produce a `SelectMany` call, which creates a single sequence from combining each element in the first sequence with each element in the second sequence. The order is important for our purposes. The first element in the first source sequence (Suits) is combined with every element in the second sequence (Ranks). This produces all thirteen cards of first suit. That process is repeated with each element in the first sequence (Suits). The end result is a deck of cards ordered by suits, followed by values.\n",
    "\n",
    "It's important to keep in mind that whether you choose to write your LINQ in the query syntax used above or use method syntax instead, it's always possible to go from one form of syntax to the other. The above query written in query syntax can be written in method syntax as:\n",
    "\n",
    "```csharp\n",
    "var sequence = Suits().SelectMany(suit => Ranks().Select(rank => new Card(suit, rank));\n",
    "```\n",
    "\n",
    "The compiler translates LINQ statements written with query syntax into the equivalent method call syntax. Therefore, regardless of your syntax choice, the two versions of the query produce the same result. Choose which syntax works best for your situation: for instance, if you're working in a team where some of the members have difficulty with method syntax, try to prefer using query syntax.\n",
    "\n",
    "## Display the deck\n",
    "\n",
    "Now that you've created the deck of cards, you can define a method that displays the deck in your notebook. The following method declares a formatter that displays the deck of cards in 4 rows of 13. It uses the method `Enumerable.Range` to count from 0 through 3. Then, is uses the `Skip` and `Take` methods to skip rows previously displayed then take the next 13 cards. The images are returned from a storage location on Azure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "Formatter<Deck>.Register((deck, writer) =>\n",
    "{\n",
    "    PocketView v = div(\n",
    "        Enumerable.Range(0,4).Select(row =>\n",
    "            div(deck.Cards.Skip(row * 13).Take(13).Select(c =>\n",
    "            {\n",
    "                var face = Enum.GetName(typeof(Rank), c.Rank);\n",
    "                var suit = Enum.GetName(typeof(Suit), c.Suit);\n",
    "                return img[src:$\"https://dotnetcards.azureedge.net/cards/{suit}{face}.png\", width:64]();\n",
    "            }))));\n",
    "    writer.Write(v);\n",
    "}, \"text/html\");\n",
    "\n",
    "Console.WriteLine(\".NET Interactive can display the deck of cards\");\n",
    "\n",
    "Deck d = new Deck();\n",
    "display(d);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Shuffle the deck\n",
    "\n",
    "There's no shuffle method to take advantage of in the standard library, so you'll have to write your own. The shuffle method you'll be creating illustrates several techniques that you'll use with LINQ-based programs, so each part of this process will be explained in steps.\n",
    "\n",
    "The first step in any good shuffle is to split the deck in two. The `Take%` and `Skip` methods that are part of the LINQ APIs provide that feature for you.\n",
    "\n",
    "In order to add some functionality to how you interact with the `IEnumerable<Card>` sequence you'll get back from LINQ queries, you'll need to write some special kinds of methods called [extension methods](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/extension-methods). Briefly, an extension method is a special purpose *static method* that adds new functionality to an already-existing type without having to modify the original type you want to add functionality to.\n",
    "\n",
    "Look at the method signature for a moment, specifically the parameters:\n",
    "\n",
    "```csharp\n",
    "static IEnumerable<T> InterleaveSequenceWith<T> (this IEnumerable<T> first, IEnumerable<T> second)\n",
    "```\n",
    "\n",
    "You can see the addition of the `this` modifier on the first argument to the method. That means you call the method as though it were a member method of the type of the first argument. This method declaration also follows a standard idiom where the input and output types are `IEnumerable<T>`. That practice enables LINQ methods to be chained together to perform more complex queries.\n",
    "\n",
    "Naturally, since you split the deck into halves, you'll need to join those halves together. In code, this means you'll be enumerating both of the sequences you acquired through `Take` and `Skip` at once, *`interleaving`* the elements, and creating one sequence: your now-shuffled deck of cards. Writing a LINQ method that works with two sequences requires that you understand how `IEnumerable<T>` works.\n",
    "\n",
    "The `System.Collections.Generic.IEnumerable<T>` interface has one method: `GetEnumerator`. The object returned by `GetEnumerator` has a method to move to the next element, and a property that retrieves the current element in the sequence. You will use those two members to enumerate the collection and return the elements. This Interleave method will be an iterator method, so instead of building a collection and returning the collection, you'll use the `yield return` syntax shown above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "static IEnumerable<T> InterleaveSequenceWith<T> (this IEnumerable<T> first, IEnumerable<T> second)\n",
    "{\n",
    "    var firstIter = first.GetEnumerator();\n",
    "    var secondIter = second.GetEnumerator();\n",
    "\n",
    "    while (firstIter.MoveNext() && secondIter.MoveNext())\n",
    "    {\n",
    "        yield return firstIter.Current;\n",
    "        yield return secondIter.Current;\n",
    "    }\n",
    "}\n",
    "\n",
    "Console.WriteLine(\".NET Interactive can shuffle the deck\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comparisons\n",
    "\n",
    "How many shuffles it takes to set the deck back to its original order? To find out, you'll need to write a method that determines if two sequences are equal. After you have that method, you'll need to place the code that shuffles the deck in a loop, and check to see when the deck is back in order.\n",
    "\n",
    "Writing a method to determine if the two sequences are equal should be straightforward. It's a similar structure to the method you wrote to shuffle the deck. Only this time, instead of `yield return`ing each element, you'll compare the matching elements of each sequence. When the entire sequence has been enumerated, if every element matches, the sequences are the same.\n",
    "\n",
    "This shows a second LINQ idiom: terminal methods. They take a sequence as input (or in this case, two sequences), and return a single scalar value. When using terminal methods, they are always the final method in a chain of methods for a LINQ query, hence the name \"terminal\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "static bool SequenceEquals<T>(this IEnumerable<T> first, IEnumerable<T> second)\n",
    "{\n",
    "    var firstIter = first.GetEnumerator();\n",
    "    var secondIter = second.GetEnumerator();\n",
    "\n",
    "    while (firstIter.MoveNext() && secondIter.MoveNext())\n",
    "    {\n",
    "        if (!firstIter.Current.Equals(secondIter.Current))\n",
    "        {\n",
    "            return false;\n",
    "        }\n",
    "    }\n",
    "\n",
    "    return true;\n",
    "}\n",
    "Console.WriteLine(\".NET Interactive can compare sequences for equality.\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can see this in action when you use it to determine when the deck is back in its original order. First, create and display the deck:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "var deck = new Deck();\n",
    "var deckDisplay = display(deck);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Put the shuffle code inside a loop, and stop when the sequence is back in its original order by applying the `SequenceEquals()` method. You can see it would always be the final method in any query, because it returns a single value instead of a sequence:\n",
    "\n",
    "Run the next cell and take note of how the deck rearranges on each shuffle. After 8 shuffles (iterations of the do-while loop), the deck returns to the original configuration it was in when you first created it from the starting LINQ query."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "int numberOfShuffles = 0;\n",
    "var shuffleDisplay = display($\"Number of shuffles: {numberOfShuffles}\");\n",
    "deckDisplay = display(deck);\n",
    "var shuffle = deck;\n",
    "do\n",
    "{\n",
    "    await Task.Delay(3000);\n",
    "    shuffle = new Deck(shuffle.Cards.Take(26).InterleaveSequenceWith(shuffle.Cards.Skip(26)));\n",
    "    numberOfShuffles++;\n",
    "    deckDisplay.Update(shuffle);\n",
    "    shuffleDisplay.Update($\"Number of shuffles: {numberOfShuffles}\");\n",
    "} while(!deck.Cards.SequenceEquals(shuffle.Cards));\n",
    "Console.WriteLine(\"Done!\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Experiment\n",
    "\n",
    "The sample you've built so far executes an *out shuffle*, where the top and bottom cards stay the same on each run. Let's make one change: we'll use an *in shuffle* instead, where all 52 cards change position. For an in shuffle, you interleave the deck so that the first card in the bottom half becomes the first card in the deck. That means the last card in the top half becomes the bottom card. This is a simple change to a singular line of code. Update the current shuffle query by switching the positions of `Take` and `Skip`. This will change the order of the top and bottom halves of the deck:\n",
    "\n",
    "```csharp\n",
    "shuffle = shuffle.Skip(26).InterleaveSequenceWith(shuffle.Take(26));\n",
    "```\n",
    "\n",
    "Run the previous cell again, and you'll see that it takes 52 iterations for the deck to reorder itself.\n",
    "\n",
    "## Conclusion\n",
    "\n",
    "In this project, you covered:\n",
    "\n",
    "- using LINQ queries to aggregate data into a meaningful sequence\n",
    "- writing Extension methods to add our own custom functionality to LINQ queries\n",
    "- locating areas in our code where our LINQ queries might run into performance issues like degraded speed\n",
    "- lazy and eager evaluation in regards to LINQ queries and the implications they might have on query performance\n",
    "\n",
    "Aside from LINQ, you learned a bit about a technique magicians use for card tricks. Magicians use the Faro shuffle because they can control where every card moves in the deck. Now that you know, don't spoil it for everyone else!\n",
    "\n",
    "For more information on LINQ, see:\n",
    "\n",
    "- [Language Integrated Query (LINQ)](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/linq/index)\n",
    "- [Introduction to LINQ](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/linq/index)\n",
    "- [Basic LINQ Query Operations (C#)](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/linq/basic-linq-query-operations)\n",
    "- [Data Transformations With LINQ (C#)](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/linq/data-transformations-with-linq)\n",
    "- [Query Syntax and Method Syntax in LINQ (C#)](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/linq/query-syntax-and-method-syntax-in-linq)\n",
    "- [C# Features That Support LINQ](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/linq/features-that-support-linq)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (C#)",
   "language": "C#",
   "name": ".net-csharp"
  },
  "language_info": {
   "file_extension": ".cs",
   "mimetype": "text/x-csharp",
   "name": "C#",
   "pygments_lexer": "csharp",
   "version": "8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
